/**
 * 图表相关逻辑
 */
import * as echarts from 'echarts/core'
import { TaskService } from '@/services/bussiness/Task/task.service'
import { initContextmenuEvent } from './dag.chart.event.contextmenu'
import {
  ChartEdgeType,
  ChartNodeType,
  LifeCycleMethodsInterface,
  OperateEdgeLogInfoType,
  OperateVertexLogInfoType,
  SetNodeStatusParams
} from './dag.chart.dto'

import { ElLoading, ElMessage, MessageHandler } from 'element-plus'
import 'element-plus/theme-chalk/el-message.css'

import {
  // 系列类型的定义后缀都为 SeriesOption
  GraphSeriesOption,
  GraphChart
} from 'echarts/charts'
import {
  TitleComponent,
  // 组件类型的定义后缀都为 ComponentOption
  TitleComponentOption,
  TooltipComponent,
  TooltipComponentOption,
  GridComponent,
  GridComponentOption,
  GraphicComponent,
  GraphicComponentOption,
  // 数据集组件
  DatasetComponent,
  DatasetComponentOption,
  // 内置数据转换器组件 (filter, sort)
  TransformComponent,
  LegendComponent
} from 'echarts/components'
import { LabelLayout, UniversalTransition } from 'echarts/features'
import { CanvasRenderer } from 'echarts/renderers'
import { TaskChartDataDto } from '@/services/bussiness/Task/task.chart.dto'
import { ECharts } from 'echarts/core'
import { OperateStackInterface } from './dag.chart.dto'
import { initClickEvent } from './dag.chart.event.click'
import { hasCycle } from './dag.chart.util'
import {
  DagTaskRegisterStatusEnum,
  DagTaskStatusEnum
} from '@/services/bussiness/Task/task.api.dto'

// 执行状态
const DagTaskStatusMap = {
  [DagTaskStatusEnum.NOT_START]: {
    name: 'NOT_START',
    color: '#909399',
    categoryIndex: 0
  },
  [DagTaskStatusEnum.WAIT]: {
    name: 'WAIT',
    color: '#E6A23C',
    categoryIndex: 1
  },
  [DagTaskStatusEnum.RUN]: {
    name: 'RUN',
    color: '#409EFF',
    categoryIndex: 2
  },
  [DagTaskStatusEnum.FAIL]: {
    name: 'FAIL',
    color: '#F56C6C',
    categoryIndex: 3
  },
  [DagTaskStatusEnum.FINISH]: {
    name: 'FINISH',
    color: '#67C23A',
    categoryIndex: 4
  }
}

// 注册状态
const DagTaskRegisterStatusMap = {
  [DagTaskRegisterStatusEnum.NOT_REGISTER]: {
    name: '未注册',
    color: '#F56C6C',
    categoryIndex: 5
  },
  [DagTaskRegisterStatusEnum.REGISTER]: {
    name: '已注册',
    color: '#67C23A',
    categoryIndex: 6
  }
}

// 通过 ComposeOption 来组合出一个只有必须组件和图表的 Option 类型
type ECOption = echarts.ComposeOption<
  | GraphSeriesOption
  | TitleComponentOption
  | TooltipComponentOption
  | GridComponentOption
  | GraphicComponentOption
  | DatasetComponentOption
>

// 注册必须的组件
echarts.use([
  TitleComponent,
  TooltipComponent,
  GridComponent,
  GraphChart,
  DatasetComponent,
  TransformComponent,
  GraphicComponent,
  LabelLayout,
  UniversalTransition,
  CanvasRenderer,
  LegendComponent
])

function generateTooltipTagList(taskInfo: any) {
  if (taskInfo) {
    const result: Array<string> = []
    for (const key in taskInfo) {
      if (key !== 'col' && key !== 'row') {
        const value = taskInfo[key]
        result.push(`<div>${key}：${value}</div>`)
      }
    }

    return result
  }
  return []
}

// 初始化图例
function getCategories() {
  const categories = []
  const legendData = []
  const legendData2 = []
  for (const item of Object.values(DagTaskStatusMap)) {
    categories[item.categoryIndex] = {
      name: item.name,
      itemStyle: {
        color: item.color
      }
    }
    // legendData[item.categoryIndex] = item.name
    legendData.push(item.name)
  }
  for (const item of Object.values(DagTaskRegisterStatusMap)) {
    categories[item.categoryIndex] = {
      name: item.name,
      itemStyle: {
        color: item.color
      }
    }
    legendData2.push(item.name)
  }
  return {
    categories,
    legend: [
      {
        data: legendData,
        show: false
      },
      {
        data: legendData2,
        show: false
      }
    ]
  }
}

// 初始化图表option
async function initChartOption(response: TaskChartDataDto) {
  const { nodes, edges, dagName, dagId } = response
  const { categories, legend } = getCategories()
  const option: ECOption = {
    title: {
      text: dagName
    },
    legend,
    tooltip: {},
    animationDurationUpdate: 1500,
    animationEasingUpdate: 'quinticInOut',
    series: [
      {
        name: 'Les Miserables',
        type: 'graph',
        layout: 'none',
        legendHoverLink: false,
        draggable: true,
        roam: true,
        // scaleLimit: {
        //   min: 0.7,
        //   max: 1.2
        // },
        categories,
        symbol: 'rect',
        symbolSize: 1,
        edgeSymbol: ['circle', 'arrow'],
        edgeSymbolSize: [4, 10],
        edgeLabel: {
          fontSize: 14,
          show: false
        },
        itemStyle: {
          color: 'transparent',
          borderColor: 'transparent'
        },
        emphasis: {
          focus: 'adjacency',
          label: {
            padding: 10,
            backgroundColor: 'rgba(255,255,255,1)',
            shadowColor: 'rgba(0,0,0,.2)',
            shadowBlur: 15
          }
        },
        tooltip: {
          formatter(params) {
            const data: any = params.data
            const taskInfo = data.taskInfo
            if (data.source) {
              // 线
              return `${data.source} > ${data.target}`
            }

            const resultList = [
              `<div style="font-weight: 700; font-size: 1rem;margin-bottom: .2rem;">${data.name}</div>`
            ]

            if (taskInfo) {
              resultList.push(...generateTooltipTagList(taskInfo))
            }
            return resultList.join('')
          }
        },
        autoCurveness: true,
        label: {
          show: true,
          width: 200,
          height: 40,
          lineHeight: 100,
          padding: 10,
          backgroundColor: 'rgba(255,255,255,.5)',
          borderColor: '#ebeef5',
          borderWidth: 1,
          borderRadius: 10,
          shadowColor: 'rgba(0,0,0,0)',
          shadowBlur: 0,
          overflow: 'truncate',
          formatter(params: any) {
            return `{a|${params.data.name}}`
          },
          rich: {
            a: {
              color: '#000',
              fontSize: '14',
              fontWeight: 700,
              lineHeight: 40
            }
          }
        },
        lineStyle: {
          opacity: 0.9,
          width: 2,
          curveness: 0.2
        },
        nodes,
        edges
      }
    ]
  }

  return {
    nodes,
    edges,
    dagName,
    option,
    dagId
  }
}

export class DagChart {
  nodes: TaskChartDataDto['nodes'] = []
  edges: TaskChartDataDto['edges'] = []
  dagName: TaskChartDataDto['dagName'] = ''
  dagId: number = -1
  chart: ECharts
  option: any = undefined
  operateStack: OperateStackInterface = {
    vertexList: [],
    edgeList: []
  }
  // 添加边相关数据
  createEdgeInfo = {
    flag: false, // 是否正在创建边
    source: '',
    target: ''
  }
  messageHandler: MessageHandler | undefined
  lifeCycleMethods: LifeCycleMethodsInterface = {
    afterInit() {}
  }

  constructor(el: HTMLCanvasElement, dagName: string) {
    this.chart = echarts.init(el)

    this.initChart(dagName)
  }

  /**
   * 初始化图表
   */
  async initChart(dagName: string) {
    const response = await TaskService.getTaskChartData(dagName)

    // 获取数据成功
    if (TaskService.isCorrectData(response)) {
      const { nodes, edges, dagName, option, dagId } = await initChartOption(response)
      this.nodes = nodes
      this.edges = edges
      this.dagName = dagName
      this.dagId = dagId
      this.option = option
      this.refreshChart()
      this.initChartEvent()
      this.lifeCycleMethods.afterInit()
      return
    }

    // 获取数据失败
    if (this.lifeCycleMethods.afterInit) {
      this.lifeCycleMethods.afterInit(response)
    }
  }

  /**
   * 初始化结束后触发
   */
  onAfterInit(callback: () => void) {
    this.lifeCycleMethods.afterInit = callback
  }

  /**
   * 初始化图表相关事件
   */
  initChartEvent() {
    // 右键点击事件
    initContextmenuEvent(this)

    // 左键点击事件
    initClickEvent(this)

    // 窗口变化时重绘
    window.onresize = () => {
      this.chart.resize()
    }
  }

  /**
   * 刷新图表
   */
  refreshChart() {
    this.chart.setOption(this.option)
  }

  /**
   * 校验 taskName
   * @param taskName
   * @returns
   */
  checkTaskName(taskName: string): boolean {
    if (this.nodes) {
      if (taskName === '') {
        ElMessage({
          type: 'warning',
          message: '请输入节点名称'
        })
        return false
      }
      const foundIndex = this.nodes.findIndex((item) => item.name === taskName)

      if (foundIndex > -1) {
        ElMessage({
          type: 'warning',
          message: '节点名称已存在'
        })
        return false
      }
    }
    return true
  }

  /**
   * 创建节点
   */
  addNode(taskName: string, x: number, y: number) {
    if (this.nodes) {
      const node: ChartNodeType = {
        name: taskName,
        x,
        y
      }

      this.nodes.push(node)
      this.refreshChart()
      this.addOperateLog('add', 'vertex', {
        taskName
      })
    }
  }

  /**
   * 删除节点
   */
  async removeNode(taskName: string) {
    if (this.nodes && this.edges) {
      const foundIndex = this.nodes.findIndex((item) => item.name === taskName)
      const foundNode = this.nodes[foundIndex]
      if (foundNode.taskInfo && foundIndex > -1) {
        const loading = ElLoading.service()
        const response = await TaskService.delVertexByTaskId(
          this.dagName,
          foundNode.taskInfo.taskId,
          taskName
        )
        loading.close()

        if (response?.result) {
          // 删除节点
          this.nodes.splice(foundIndex, 1)
          this.addOperateLog('delete', 'vertex', {
            taskName
          })

          // 删除节点相关的边
          let length = this.edges.length
          while (length--) {
            const item = this.edges[length]
            if (item.source === taskName || item.target === taskName) {
              this.removeEdge(item.source as string, item.target as string)
            }
          }

          this.refreshChart()
          ElMessage({
            type: 'success',
            message: '删除节点成功'
          })
        } else {
          ElMessage({
            type: 'warning',
            message: response?.msg || '删除节点失败'
          })
        }
      } else {
        ElMessage({
          type: 'warning',
          message: '节点信息不全，无法删除'
        })
      }
    }
  }

  /**
   * 修改节点名称
   */
  async editNodeName(oldTaskName: string, newTaskName: string) {
    if (this.nodes && this.edges) {
      const foundIndex = this.nodes.findIndex((item) => item.name === oldTaskName)
      if (foundIndex > -1) {
        // 修改节点名称
        const item = this.nodes[foundIndex]

        if (!item.taskInfo) {
          ElMessage({
            message: '节点信息缺失',
            type: 'warning'
          })
          return
        }
        // const response = await TaskService.updateTaskName(item.taskInfo.id, newTaskName)

        // console.log(811, response)

        item.name = newTaskName

        // 修改节点相关的边
        let length = this.edges.length
        while (length--) {
          const item = this.edges[length]
          if (item.source === oldTaskName) {
            item.source = newTaskName
          }
          if (item.target === oldTaskName) {
            item.target = newTaskName
          }
        }
        this.refreshChart()
      }
    }
  }

  /**
   * 添加边：开始
   */
  addEdgeStart(taskName: string) {
    this.messageHandler = ElMessage({
      showClose: true,
      message: `开始节点为：${taskName}，请选择目标节点`,
      duration: 0,
      onClose: this.addEdgeStop.bind(this)
    })
    this.createEdgeInfo.flag = true
    this.createEdgeInfo.source = taskName
  }

  /**
   * 添加边：结束
   */
  addEdgeEnd(nextTaskName: string) {
    if (this.edges) {
      // 是否指向自己
      if (nextTaskName === this.createEdgeInfo.source) {
        ElMessage({
          type: 'error',
          message: '不能将自己设置为目标节点'
        })
        return
      }

      const newEdge = {
        source: this.createEdgeInfo.source,
        target: nextTaskName
      }

      // 是否已存在该边
      const foundIndex = this.edges.findIndex(
        (item) => item.source === newEdge.source && item.target === newEdge.target
      )

      if (foundIndex > -1) {
        ElMessage({
          type: 'error',
          message: '已存在到目标的边'
        })
        return
      }

      // 判断是否有环
      const newEdges: Array<ChartEdgeType> = []

      newEdges.push(...this.edges, newEdge)

      const hasLoop = hasCycle(newEdges)
      if (hasLoop) {
        ElMessage({
          type: 'error',
          message: '存在环路，无法将该节点设置为目标节点'
        })
        return
      }

      this.addEdgeItem(newEdge.source, newEdge.target)
      this.addEdgeStop()
    }
  }

  /**
   * 添加边：停止
   */
  addEdgeStop() {
    this.createEdgeInfo.flag = false
    this.createEdgeInfo.source = ''
    this.createEdgeInfo.target = ''
    this.messageHandler?.close()
  }

  /**
   * 添加边元素
   */
  addEdgeItem(taskName: string, nextTaskName: string) {
    if (this.edges) {
      this.edges.push({
        source: taskName,
        target: nextTaskName
      })
      this.refreshChart()
      this.addOperateLog('add', 'edge', {
        taskName,
        nextTaskName
      })
    }
  }

  /**
   * 删除边
   */
  removeEdge(taskName: string, nextTaskName: string) {
    if (this.edges) {
      const foundIndex = this.edges.findIndex(
        (item) => item.source === taskName && item.target === nextTaskName
      )
      if (foundIndex > -1) {
        this.edges.splice(foundIndex, 1)
        this.refreshChart()
        this.addOperateLog('delete', 'edge', {
          taskName,
          nextTaskName
        })
      }
    }
  }

  /**
   * 设置节点状态
   */
  setNodeStatus(statusInfo: Array<SetNodeStatusParams>) {
    this.clearNodeStatus()

    this.nodes.forEach((item: any) => {
      const statusFound = statusInfo.find((i) => i.taskId === item?.taskInfo?.taskId)

      if (statusFound) {
        const color = DagTaskStatusMap[statusFound.status].color
        const name = DagTaskStatusMap[statusFound.status].name
        item.category = DagTaskStatusMap[statusFound.status].categoryIndex
        item.label = {
          borderColor: color,
          rich: {
            a: {
              color: color,
              fontSize: '14',
              fontWeight: 700,
              lineHeight: 40
            }
          }
        }
        item.tooltip = {
          formatter(params: any) {
            const data: any = params.data
            if (data.source) {
              // 线
              return `${data.source} > ${data.target}`
            }
            const taskInfo = data.taskInfo
            const resultList = [
              `<div style="font-weight: 700; font-size: 1rem;margin-bottom: .2rem;">【${name}】${data.name}</div>`
            ]

            if (taskInfo) {
              resultList.push(...generateTooltipTagList(taskInfo))
            }
            return resultList.join('')
          }
        }
      }
    })
    // 展示图例
    this.option.legend[0].show = true
    this.refreshChart()
  }

  /**
   * 设置节点注册状态
   */
  setNodeRegisterStatus(registerTaskNameList: Array<string>) {
    this.clearNodeStatus()
    this.nodes.forEach((item: any) => {
      const statusFound = registerTaskNameList.find((i) => i === item?.name)
      let color = DagTaskRegisterStatusMap[DagTaskRegisterStatusEnum.NOT_REGISTER].color
      let name = DagTaskRegisterStatusMap[DagTaskRegisterStatusEnum.NOT_REGISTER].name
      item.category = DagTaskRegisterStatusMap[DagTaskRegisterStatusEnum.NOT_REGISTER].categoryIndex
      if (statusFound) {
        color = DagTaskRegisterStatusMap[DagTaskRegisterStatusEnum.REGISTER].color
        name = DagTaskRegisterStatusMap[DagTaskRegisterStatusEnum.REGISTER].name
        item.category = DagTaskRegisterStatusMap[DagTaskRegisterStatusEnum.REGISTER].categoryIndex
      }
      item.label = {
        borderColor: color,
        rich: {
          a: {
            color: color,
            fontSize: '14',
            fontWeight: 700,
            lineHeight: 40
          }
        }
      }
      item.tooltip = {
        formatter(params: any) {
          const data: any = params.data
          if (data.source) {
            // 线
            return `${data.source} > ${data.target}`
          }
          const taskInfo = data.taskInfo
          const resultList = [
            `<div style="font-weight: 700; font-size: 1rem;margin-bottom: .2rem;">【${name}】${data.name}</div>`
          ]

          if (taskInfo) {
            resultList.push(...generateTooltipTagList(taskInfo))
          }
          return resultList.join('')
        }
      }
    })
    // 展示图例
    this.option.legend[1].show = true
    this.refreshChart()
  }

  /**
   * 清除节点状态
   */
  clearNodeStatus() {
    this.nodes.forEach((item: any) => {
      delete item.label
      delete item.category
      delete item.tooltip
    })
    // 隐藏图例
    this.option.legend[0].show = false
    this.option.legend[1].show = false
    this.refreshChart()
  }

  /**
   * 根据taskId获取taskName
   */
  getTaskNameByTaskId(taskId: number) {
    const foundNode = this.nodes.find((item) => item?.taskInfo?.taskId === taskId)
    if (foundNode) {
      return foundNode.name
    }
  }

  /**
   * 添加操作记录
   */
  addOperateLog(
    operateType: 'add' | 'delete',
    targetType: 'vertex',
    info: OperateVertexLogInfoType
  ): void
  addOperateLog(
    operateType: 'add' | 'delete',
    targetType: 'edge',
    info: OperateEdgeLogInfoType
  ): void
  addOperateLog(operateType: any, targetType: any, info: any) {
    const operateLog = {
      type: operateType,
      dagName: this.dagName,
      ...info
    }
    if (targetType === 'vertex') {
      if (operateType === 'delete') {
        // 删除新创建元素时，删除之前的添加log
        const foundIndex = this.operateStack.vertexList.findIndex(
          (item) => item.taskName === operateLog.taskName && item.type === 'add'
        )

        if (foundIndex > -1) {
          this.operateStack.vertexList.splice(foundIndex, 1)
          return
        }
      }
      this.operateStack.vertexList.push(operateLog)
    } else if (targetType === 'edge') {
      if (operateType === 'delete') {
        // 删除新创建元素时，删除之前的添加log
        const foundIndex = this.operateStack.edgeList.findIndex(
          (item) => item.taskName === operateLog.taskName && item.type === 'add'
        )

        if (foundIndex > -1) {
          this.operateStack.edgeList.splice(foundIndex, 1)
          return
        }
      }
      this.operateStack.edgeList.push(operateLog)
    }
  }
}
